let _im = {
    getInstance: () => {
        let instance = {
            getCoords: () => {
                //...业务逻辑
                return [[[5, 5], [5, 15], [15, 15], [15, 5]], [[20, 15], [20, 20], [30, 15]]];
            }
        };
    },//获取实例
    xxx: undefined,//
};
//!!!!!完善注释
//!!!!!!!!!!!!!!!!!!!cavas!!!!!!!!!!!!!!!!!!!
//init-param e.g. init to show menu, scale is 1.2
let doc;//文档
let body;//主体
let onceLoading;//加载
let baseMap;//底图
let markerBox;//标记盒
let markerPen;//记号笔
let maskLayer;//遮罩层
let markerImgIcon = {
    mark: 'img/marker-22-ff0000.png',//标记时
    fixed: 'img/marker-22-ff0000.png',//固定时
    hover: 'img/marker-22-23c08c.png',//悬停时
    active: 'img/marker-22-1296db.png'//激活时
};//标记图像
let coordsList = [];
let coordsIndex = 0;
let bgPos;//背景位置
let cursorPointer;//鼠标指针位点
let cursorTracker;//光标追踪器
let coefficientBox;//系数盒
let qCoords;//系数盒-坐标
let qOffset;//系数盒-偏移
let qPosition;//系数盒-位置
let qLatest;//系数盒-最近
let qTotal;//系数盒-数量
let centralArea;//中央区域
let curMarker;//当前标记
let areaBox;//区域盒
let smallMap;//小地图
let expandRegion;//膨胀区域
let windowRegion;//窗口区域
let imageRegion;//图像区域
let baseImg;//基础图像
let menuBox;//菜单盒
let menuPanel;//菜单面板
let oriPos;//原始位置
let isDragged = false;//是否拖拽过
let isDragging = false;//是否拖拽中
let isMarking = false;//是否标记中
let isSetting = false;//是否设置中
let isTipping = false;//是否提示中
let isRippling = true;//是否涟漪中
let isCoefficientBox = true;//是否显示系数盒
let isAnimation = true;//是否显示动画
let showCursorPointer = false;//是否显示鼠标指针位点
let pickThreshold = -1;//-1 任意 0 禁止 1+ 限制
let bjwzSlot;//标记位置按钮
let xszbSlot;//显示坐标按钮
let xsdhSlot;//显示动画按钮
let dtxsSlot;//动态显示按钮
let jzdqSlot;//居中对齐按钮
let ksdwSlot;//快速定位按钮
let scwzSlot;//删除位置按钮
let scaleValue = 1;//缩尺（默认像素-厘米）
let scaleUnit = 1;//缩尺（默认像素-厘米）
let zoomStep = 0.125;//缩放步长
let zoomLevel = 1;//缩放级别
let zoomInThreshold = 8;//放大阈值
let zoomOutThreshold = 0.125;//缩小阈值
let sffs = 2;//缩放方式
let hzfs = 2;//绘制方式
let panel;//面板
let mask;//蒙板
let info;//信息
let imageUrl = 'img/sample.png';
let imageSize;//图像尺寸
let backgroundUrl = 'img/bg.png';
let coordsType = 'OBJECT';//OBJECT/STRING
let tipHtml;
let tipStyle;
let hoverFlag = true;//悬浮标志
let allowZoom = true;//允许缩放
let allowDrag = true;//允许拖拽
let allowMenu = true;//允许菜单
let allowMark = true;//允许标记
let allowRemove = true;//允许删除
let allowDerail = true;//允许出轨
let autoFocus = true;//自动聚焦
let autoBack = true;//自动回弹
let isCenterInfo = false;//是否居中信息
let closeViaBtn = false;//通过按钮关闭
let whenReady = noop;
let whenChange = noop;
let whenClick = noop;
let whenHidden = noop;
let imObj = {
    inspectCoords: inspectCoords,
    getCoordsType: getCoordsType,
    getSize: getSize,
    gotoCoords: gotoCoords,
    getCoords: getCoords,
    setCoords: setCoords,
    findCoords: findCoords,
    hasCoords: hasCoords,
    findMarker: findMarker,
    clearCoords: clearCoords,
    removeCoords: removeCoords,
    switchToDragMode: switchToDragModeEnforce,
    alignCenter: alignCenter,
    getMarkerBox: () => markerBox,
    pickCoords: (threshold, coords, markNow) => {
        pickThreshold = Number(threshold);
        setCoords(coords);
        syncTextForTotal();
        if (allowMark && pickThreshold != 0 && markNow) {
            switchToDragModeEnforce();
            addClass(bjwzSlot, 'checked');
            isMarking = true;
            addClass(markerBox, 'marking');
            doc.style.cursor = 'default';
            show(markerBox);
            show(markerPen);
            setPositioning(markerPen, {
                x: -markerImgIcon.fixed.width,
                y: -markerImgIcon.fixed.height
            });
        }
    }
};

function initImageMarking(config) {
    if (config.coordsType != undefined) {
        coordsType = config.coordsType;
    }
    if (config.imageUrl != undefined) {
        imageUrl = config.imageUrl;
    }
    if (config.backgroundUrl != undefined) {
        backgroundUrl = config.backgroundUrl;
    }
    if (config.tipHtml != undefined) {
        tipHtml = config.tipHtml;
    }
    if (config.tipStyle != undefined) {
        tipStyle = config.tipStyle;
    }
    if (config.hoverFlag != undefined) {
        hoverFlag = config.hoverFlag;
    }
    if (config.closeViaBtn != undefined) {
        closeViaBtn = config.closeViaBtn;
    }
    if (config.zoomStep != undefined) {
        zoomStep = config.zoomStep;
    }
    if (config.zoomLevel != undefined) {
        zoomLevel = config.zoomLevel;
    }
    if (config.zoomInThreshold != undefined) {
        zoomInThreshold = config.zoomInThreshold;
    }
    if (config.zoomOutThreshold != undefined) {
        zoomOutThreshold = config.zoomOutThreshold;
    }
    if (config.allowDrag != undefined) {
        allowDrag = config.allowDrag;
    }
    if (config.allowZoom != undefined) {
        allowZoom = config.allowZoom;
    }
    if (config.allowMenu != undefined) {
        allowMenu = config.allowMenu;
    }
    if (config.allowMark != undefined) {
        allowMark = config.allowMark;
    }
    if (config.allowRemove != undefined) {
        allowRemove = config.allowRemove;
    }
    if (config.allowDerail != undefined) {
        allowDerail = config.allowDerail;
    }
    if (config.autoFocus != undefined) {
        autoFocus = config.autoFocus;
    }
    if (config.autoBack != undefined) {
        autoBack = config.autoBack;
    }
    if (config.isCenterInfo != undefined) {
        isCenterInfo = config.isCenterInfo;
    }
    if (config.showCoefficient != undefined) {
        isCoefficientBox = config.showCoefficient;
    }
    if (config.showAnimation != undefined) {
        isAnimation = config.showAnimation;
    }
    if (config.showCursorPointer != undefined) {
        showCursorPointer = config.showCursorPointer;
    }
    if (config.rippling != undefined) {
        isRippling = config.rippling;
    }
    if (config.markerImgIcon != undefined) {
        markerImgIcon = config.markerImgIcon;
    }
    if (isFunction(config.whenReady)) {
        whenReady = config.whenReady;
    }
    if (isFunction(config.whenChange)) {
        whenChange = config.whenChange;
    }
    if (isFunction(config.whenClick)) {
        whenClick = config.whenClick;
    }
    if (isFunction(config.whenHidden)) {
        whenHidden = config.whenHidden;
    }
    //检查配置
    if (coordsType != 'OBJECT' && coordsType != 'STRING') {
        throw 'invalid coordsType';
    }
    if (zoomInThreshold < zoomOutThreshold || zoomOutThreshold <= 0) {
        throw 'invalid zoomThreshold';
    }
    configInitialized();
}

function configInitialized() {
    //缓存节点
    doc = document.documentElement;
    body = document.body;
    baseMap = document.getElementById('baseMap');
    onceLoading = document.getElementById('onceLoading');
    markerBox = document.getElementById('markerBox');
    markerPen = document.getElementById('markerPen');
    cursorPointer = document.getElementById('cursorPointer');
    cursorTracker = document.getElementById('cursorTracker');
    coefficientBox = document.getElementById('coefficientBox');
    qCoords = coefficientBox.querySelector('.coords');
    qOffset = coefficientBox.querySelector('.offset');
    qPosition = coefficientBox.querySelector('.position');
    qLatest = coefficientBox.querySelector('.latest');
    qTotal = coefficientBox.querySelector('.total');
    centralArea = document.getElementById('centralArea');
    maskLayer = document.getElementById('maskLayer');
    menuBox = document.getElementById('menuBox');
    menuPanel = menuBox.querySelector('.panel');
    //areaBox = document.getElementById('areaBox');
    //smallMap = document.getElementById('smallMap');
    //expandRegion = smallMap.querySelector('.expand-region');
    //windowRegion = smallMap.querySelector('.window-region');
    //imageRegion = smallMap.querySelector('.image-region');
    bjwzSlot = menuBox.querySelector('.slot > .bjwz');
    xszbSlot = menuBox.querySelector('.slot > .xszb');
    xsdhSlot = menuBox.querySelector('.slot > .xsdh');
    dtxsSlot = menuBox.querySelector('.slot > .dtxs');
    jzdqSlot = menuBox.querySelector('.slot > .jzdq');
    ksdwSlot = menuBox.querySelector('.slot > .ksdw');
    scwzSlot = menuBox.querySelector('.slot > .scwz');
    //禁止右键
    doc.oncontextmenu = () => false;
    //禁止选择
    doc.onselectstart = () => false;
    //遮罩层的一次性事件：解决鼠标在坐标点上直接刷新，点击无效。
    let one_maskLayer = () => {
        //TODO:待实现
        //docMouseDown();
        //docMouseMove();
        //docMouseUp();
        re(maskLayer, dom_obj_event.click, one_maskLayer);
    };
    ae(maskLayer, dom_obj_event.click, one_maskLayer);
    //自适应
    //window.onresize = function () {
    //    //let dimension = {
    //    //    width: doc.clientWidth,
    //    //    height: doc.clientHeight
    //    //};
    //    //setDimension(maskLayer, dimension);
    //    //setDimension(markerBox, dimension);
    //    //setDimension(areaBox,dimension);
    //    //setDimension(menuBox, dimension);
    //    //syncSmallMap();
    //};
    //手动触发
    //window.onresize();
    //不允许拖拽
    if (!allowDrag) {
        doc.style.cursor = 'default';
    }
    //悬浮时不提示
    if (!hoverFlag) {
        addClass(markerBox, 'notip');
    }
    //设置背景
    setBackgroundImage(body, backgroundUrl);
    //设置底图
    loadImage(imageUrl, (img, src, width, height) => {
        imageSize = {
            width: width,
            height: height
        };
        setBackgroundImage(baseMap, src);
        baseMap.style.backgroundSize = width + 'px ' + height + 'px';
        let position = {
            x: (doc.clientWidth - width) / 2,
            y: (doc.clientHeight - height) / 2
        };
        setBackgroundPosition(baseMap, position);
        setPositioning(markerBox, position);
        syncTextForOffset();
        bgPos = position;
        baseImg = {
            width: width,
            height: height
        };
        //载入配置
        if (isCoefficientBox) {
            show(coefficientBox);
            addClass(xszbSlot, 'checked');
        }
        if (isAnimation) {
            addClass(xsdhSlot, 'checked');
        }
        if (showCursorPointer) {
            addClass(cursorPointer, 'shown');
        }
        if (isRippling) {
            addClass(markerBox, 'rippling');
            addClass(dtxsSlot, 'checked');
        }
        //gotoCoords(0);
        //window.onresize();
        registerDocumentEvents();
        initMarkerImgIcon(() => {
            whenReady(imObj);
            syncTextForTotal();
            onceLoading.remove();
        });
    });
}

//注册文档事件
function registerDocumentEvents() {
    //鼠标滚动缩放
    if (allowZoom) {
        doc.onwheel = docWheel;
    }
    //键盘松开操作
    doc.onkeyup = docKeyUp;
    //鼠标移动操作
    doc.onmousemove = docMouseMove;
    //鼠标按下操作
    doc.onmousedown = docMouseDown;
    //鼠标放开操作
    doc.onmouseup = docMouseUp;
    //绑定事件-切换拖拽模式
    baseMap.onclick = () => {
        if (!isMarking && !closeViaBtn) {
            switchToDragMode();
        }
    };
    coefficientBox.onclick = () => {
        if (!isMarking && !closeViaBtn) {
            switchToDragMode();
        }
    };
}

function showHelp() {
    log('F1', '打印帮助');
    log('Escape', '切换到拖拽模式');
    log('Enter', '打印坐标集合');
}

function syncSmallMap() {
    let width = 0;
    width = Math.max(width, doc.clientWidth);
    width = Math.max(width, baseImg.width);
    width = Math.max(width, baseImg.width + numberOfPixel(body.style.backgroundPositionX));
    let height = 0;
    height = Math.max(height, doc.clientHeight);
    height = Math.max(height, baseImg.height);
    height = Math.max(height, baseImg.height + numberOfPixel(body.style.backgroundPositionY));
    let base = Math.max(width, height) / smallMap.offsetWidth;
    setDimension(expandRegion, {
        width: width / base - 2,
        height: height / base - 2
    });
    setDimension(windowRegion, {
        width: doc.clientWidth / base - 2,
        height: doc.clientHeight / base - 2
    });
    setDimension(imageRegion, {
        width: baseImg.width / base - 2,
        height: baseImg.height / base - 2
    });
    imageRegion.style.left = numberOfPixel(body.style.backgroundPositionX) / base + 'px';
    imageRegion.style.top = numberOfPixel(body.style.backgroundPositionY) / base + 'px';
}

function syncMarkerBox() {
}

function syncTextForOffset() {
    qOffset.textContent = numberOfPixel(baseMap.style.backgroundPositionX) + ', ' + numberOfPixel(baseMap.style.backgroundPositionY);
}

function syncTextForTotal() {
    if (pickThreshold > -1) {
        if (pickThreshold) {
            qTotal.textContent = coordsList.length + '/' + pickThreshold;
        } else {
            qTotal.textContent = 'N/A';
        }
    } else {
        qTotal.textContent = coordsList.length;
    }
}

function initMarkerImgIcon(callback) {
    let fn;
    fn = () => {
        for (let icon in markerImgIcon) {
            if (typeof markerImgIcon[icon] == 'string') {
                loadImage(markerImgIcon[icon], (img, src, width, height) => {
                    markerImgIcon[icon] = img;
                    fn();
                });
                return;
            }
        }
        //加载完毕
        //覆盖样式
        let style = dce('style');
        let css;
        //标记时
        css = '#markerPen {';
        css += 'background-image: url("' + markerImgIcon.mark.src + '");';
        css += 'background-size: ' + markerImgIcon.mark.width + 'px ' + markerImgIcon.mark.height + 'px;';
        css += 'width: ' + markerImgIcon.mark.width + 'px;';
        css += 'height: ' + markerImgIcon.mark.height + 'px;';
        css += '}';
        style.append(css);
        //固定时
        css = '#markerBox > .marker {';
        css += 'background-image: url("' + markerImgIcon.fixed.src + '");';
        css += 'background-size: ' + markerImgIcon.fixed.width + 'px ' + markerImgIcon.fixed.height + 'px;';
        css += 'width: ' + markerImgIcon.fixed.width + 'px;';
        css += 'height: ' + markerImgIcon.fixed.height + 'px;';
        css += '}';
        style.append(css);
        //悬停时
        css = '#markerBox:not(.setting) > .marker.hover:not(.persisted) {';
        css += 'background-image: url("' + markerImgIcon.hover.src + '");';
        css += 'background-size: ' + markerImgIcon.hover.width + 'px ' + markerImgIcon.hover.height + 'px;';
        css += 'width: ' + markerImgIcon.hover.width + 'px;';
        css += 'height: ' + markerImgIcon.hover.height + 'px;';
        css += '}';
        style.append(css);
        //激活时
        css = '#markerBox > .marker.persisted {';
        css += 'background-image: url("' + markerImgIcon.active.src + '");';
        css += 'background-size: ' + markerImgIcon.active.width + 'px ' + markerImgIcon.active.height + 'px;';
        css += 'width: ' + markerImgIcon.active.width + 'px;';
        css += 'height: ' + markerImgIcon.active.height + 'px;';
        css += '}';
        style.append(css);
        //其它相关样式
        //在坐标上点击右键
        css = '#markerBox > .marker.active >.ripple {';
        css += 'width: ' + markerImgIcon.fixed.width + 'px;';
        css += 'height: ' + markerImgIcon.fixed.height + 'px;';
        css += 'left: 0;';
        css += 'top: ' + markerImgIcon.fixed.height / 2 + 'px;';
        css += '}';
        style.append(css);
        //动态显示
        css = '#markerBox:not(.setting, .tipping).rippling > .marker > .ripple {';
        css += 'width: ' + markerImgIcon.fixed.width + 'px;';
        css += 'height: ' + markerImgIcon.fixed.height + 'px;';
        css += 'left: 0;';
        css += 'top: ' + markerImgIcon.fixed.height / 2 + 'px;';
        css += '}';
        style.append(css);
        //默认提示信息
        css = '#markerBox > .marker > .info {';
        //css += 'left: ' + (2-markerImgIcon.fixed.width / 2) + 'px;';
        css += 'left: 0;';
        css += 'bottom: ' + markerImgIcon.fixed.height + 'px;';
        css += '}';
        style.append(css);
        //默认提示信息-箭头
        css = '#markerBox > .marker > .info > .arrow {';
        css += 'margin-left: ' + markerImgIcon.fixed.width / 2 + 'px;';
        css += '}';
        style.append(css);
        //用户提示信息
        css = '#markerBox > .marker > .info-user {';
        css += 'left: ' + markerImgIcon.fixed.width / 2 + 'px;';
        css += 'bottom: ' + markerImgIcon.fixed.height + 'px;';
        css += '}';
        style.append(css);
        body.appendChild(style);
        if (callback) {
            callback();
            //从父窗体获取样式
            if (tipStyle) {
                body.appendChild(parent.document.getElementById(tipStyle));
            }
        }
    };
    fn();
}

function getCoordsType() {
    return coordsType;
}

function appendMarker(position) {
    let marker = dce('div');
    marker.onclick = doTipping;
    addClass(marker, 'marker');
    setPositioning(marker, position);
    markerBox.appendChild(marker);
    let ripple = dce('div');
    addClass(ripple, 'ripple');
    marker.appendChild(ripple);
    let rippleInner = dce('div');
    addClass(rippleInner, 'ripple');
    addClass(rippleInner, 'inner');
    marker.appendChild(rippleInner);
    //创建标记
    let badges = dce('p');
    addClass(badges, 'badges');
    marker.appendChild(badges);
    //ae(badges, dom_obj_event.click, ()=>event.stopPropagation());
    //若用户未定义模板，则创建默认的提示面板
    let info = dce('div');
    ae(info, dom_obj_event.click, () => event.stopPropagation());
    if (tipHtml != undefined) {
        addClass(info, 'info-user');
        let closeBtn = '<div class="close-btn" onclick="switchToDragMode()"></div>';
        info.innerHTML = closeViaBtn ? closeBtn + tipHtml : tipHtml;
    } else {
        addClass(info, 'info');
        let content = dce('div');
        addClass(content, 'content');
        info.appendChild(content);
        let horn = dce('div');
        addClass(horn, 'horn');
        content.appendChild(horn);
        let coords = dce('div');
        addClass(coords, 'coords');
        content.appendChild(coords);
        let xy = dce('p');
        xy.textContent = JSON.stringify(position);
        addClass(xy, 'xy');
        coords.appendChild(xy);
        let arrow = dce('div');
        addClass(arrow, 'arrow');
        info.appendChild(arrow);
    }
    marker.appendChild(info);
    setAttr(marker, 'index', coordsIndex);
    coordsList[coordsIndex] = position;
    coordsIndex++;
    syncTextForTotal();
    whenChange('APPEND');
    //marker.onmouseup = function () {
    //    this.classList.add('editing');
    //    info.style.display = '';
    //};
}

function removeCoords(index) {
    if (!hasCoords(index)) {
        return false;
    }
    markerBox.querySelector('.marker[index="' + index + '"]').remove();
    coordsList.splice(index, 1);
    coordsIndex--;
    for (let i = index; i < coordsList.length; i++) {
        setAttr(markerBox.querySelector('.marker[index="' + (i + 1) + '"]'), 'index', i);
    }
    syncTextForTotal();
    whenChange('REMOVE');
    return true;
}

function clearCoords() {
    coordsList.splice(0, coordsList.length);
    coordsIndex = 0;
    let nodeList = markerBox.querySelectorAll('.marker');
    for (let i = 0; i < nodeList.length; i++) {
        nodeList[i].remove();
    }
    syncTextForTotal();
    whenChange('CLEAR');
}

function gotoCoords(index) {
    if (!hasCoords(index)) {
        return false;
    }
    let coords = findCoords(index);
    //理论上，不会出现 NaN
    //log('gotoCoords', index + ' (' + coords.x + ', ' + coords.y + ')');
    let marker = markerBox.querySelector('.marker[index="' + index + '"]');
    let position = {
        x: (doc.clientWidth - markerImgIcon.fixed.width) / 2 - (numberOfPixel(marker.style.left) + numberOfPixel(baseMap.style.backgroundPositionX)) + numberOfPixel(baseMap.style.backgroundPositionX),
        y: (doc.clientHeight - markerImgIcon.fixed.height) / 2 - (numberOfPixel(marker.style.top) + numberOfPixel(baseMap.style.backgroundPositionY)) + numberOfPixel(baseMap.style.backgroundPositionY)
    };
    bgPos = position;
    if (!isAnimation) {
        setBackgroundPosition(baseMap, position);
        syncTextForOffset();
        setPositioning(markerBox, position);
        return true;
    }
    newSimpleAnimation(baseMap, [
        {
            backgroundPosition: position.x + 'px ' + position.y + 'px'
        }
    ], {
        duration: 1e3,
        easing: 'cubic-bezier(.68,-0.55,.27,1.55)'
    }, () => {
        setBackgroundPosition(baseMap, position);
        syncTextForOffset();
    });
    newSimpleAnimation(markerBox, [
        {
            left: position.x + 'px',
            top: position.y + 'px'
        }
    ], {
        duration: 1e3,
        easing: 'cubic-bezier(.68,-0.55,.27,1.55)'
    }, () => setPositioning(markerBox, position));
    setPositioning(centralArea, {
        x: (doc.clientWidth - 48) / 2,
        y: (doc.clientHeight - 48) / 2
    });
    newSimpleAnimation(centralArea, [
        {
            transform: 'scale(0.125)'
        }, {
            opacity: 0,
            transform: 'scale(1)',
            visibility: 'visible'
        }
    ], {
        duration: 256,
        delay: 1e3,
        iterations: 3
    });
    return true;
}

function doTipping() {
    if (hasClass(this, 'persisted')) {
        switchToDragMode();
    } else {
        let marker = markerBox.querySelector('.marker.persisted');
        removeClass(marker, 'persisted');
        whenHidden(marker);
        addClass(this, 'persisted');
        whenClick(this);
        if (isCenterInfo) {
            centerInfo(this);
        } else if (autoFocus) {
            focusMarker(this);
        }
    }
}

function switchToDragMode() {
    if (allowDrag) {
        doc.style.cursor = '';
    }
    if (isSetting) {
        closeMenu();
    } else if (isMarking) {
        isMarking = false;
        hide(markerPen);
        removeClass(bjwzSlot, 'checked');
        removeClass(markerBox, 'marking');
    } else if (isTipping) {
        isTipping = false;
        removeClass(markerBox, 'tipping');
        let marker = markerBox.querySelector('.marker.persisted');
        if (marker != undefined) {
            removeClass(marker, 'persisted');
            whenHidden(marker);
        }
        show(maskLayer);
    }
}

function switchToDragModeEnforce() {
    if (allowDrag) {
        doc.style.cursor = '';
    }
    if (isSetting) {
        closeMenu();
    }
    if (isMarking) {
        isMarking = false;
        hide(markerPen);
        removeClass(bjwzSlot, 'checked');
        removeClass(markerBox, 'marking');
    }
    if (isTipping) {
        isTipping = false;
        removeClass(markerBox, 'tipping');
        let marker = markerBox.querySelector('.marker.persisted');
        if (marker != undefined) {
            removeClass(marker, 'persisted');
            whenHidden(marker);
        }
        show(maskLayer);
    }
}

function inspectCoords(type) {
    log('inspectCoords', type);
    if (coordsList.length) {
        if (type == undefined || type == 0) {
            let index = 0;
            setInterval(() => {
                if (index == coordsList.length) {
                    index = 0;
                }
                gotoCoords(index++);
            }, 3e3);
        } else if (type == 1) {
            let index = 0;
            setInterval(() => {
                gotoCoords(index++ % 2);
            }, 2e3);
        } else if (type == 2) {
            let flag = false;
            setInterval(() => {
                gotoCoords((flag = !flag) ? 0 : coordsList.length - 1);
            }, 2e3);
        }
    }
}

function makeCoordsExpression() {
    let a = [];
    for (let i = 0; i < coordsList.length; i++) {
        let c = coordsList[i];
        a.push('{x: ' + c.x + ', y: ' + c.y + '}');
    }
    let s = '[' + a.join(', ') + ']';
    log('coordsList = ' + s + ';');
    return s;
}

function getSize() {
    return coordsList.length;
}

function getIndex(marker) {
    return Number(getAttr(marker, 'index'));
}

function getCoords() {
    if (coordsType == 'OBJECT') {
        return [].concat(coordsList);
    }
    let a = [];
    for (let i = 0; i < coordsList.length; i++) {
        let c = coordsList[i];
        a.push(c.x + ',' + c.y);
    }
    return a.join(';');
}

function setCoords(coords) {
    if (!chkCoords(coords)) {
        alert('坐标集合包含重复元素！');
        return;
    }
    clearCoords();
    if (coordsType == 'OBJECT') {
        for (let i = 0; i < coords.length; i++) {
            if (pickThreshold < 0 || i < pickThreshold) {
                appendMarker(coords[i]);
            }
        }
    } else if (coords != '') {
        let rows = coords.split(';');
        for (let i = 0; i < rows.length; i++) {
            let cols = rows[i].split(',');
            if (pickThreshold < 0 || i < pickThreshold) {
                appendMarker({x: Number(cols[0]), y: Number(cols[1])});
            }
        }
    }
}

function chkCoords(coords) {
    let a = [];
    if (coordsType == 'OBJECT') {
        for (let i = 0; i < coords.length; i++) {
            let c = coords[i];
            let e = c.x + ',' + c.y;
            if (a.includes(e)) {
                return false;
            }
            a.push(e);
        }
    } else if (coords != '') {
        let rows = coords.split(';');
        for (let i = 0; i < rows.length; i++) {
            let cols = rows[i].split(',');
            let c = {x: Number(cols[0]), y: Number(cols[1])};
            let e = c.x + ',' + c.y;
            if (a.includes(e)) {
                return false;
            }
            a.push(e);
        }
    }
    return true;
}

function findCoords(index) {
    return coordsList[index];
}

function hasCoords(index) {
    return findCoords(index) != undefined;
}

function findMarker(x, y) {
    for (let i = 0; i < coordsList.length; i++) {
        let c = coordsList[i];
        if (c.x == x && c.y == y) {
            return markerBox.querySelector('.marker[index="' + i + '"]');
        }
    }
}

function closeMenu() {
    hide(menuBox);
    isSetting = false;
    removeClass(markerBox, 'setting');
    let marker = markerBox.querySelector('.marker.active');
    if (marker != undefined) {
        removeClass(marker, 'active');
        return marker;
    }
}

function docMouseMove(e) {
    if (isCoefficientBox) {
        qCoords.textContent = event.clientX + ', ' + event.clientY;
        qPosition.textContent = (event.clientX - markerImgIcon.fixed.width / 2 - numberOfPixel(baseMap.style.backgroundPositionX)) + ', ' + (event.clientY - markerImgIcon.fixed.height - numberOfPixel(baseMap.style.backgroundPositionY));
    }
    if (showCursorPointer) {
        setPositioning(cursorPointer, {
            x: event.clientX - cursorPointer.clientWidth / 2,
            y: event.clientY - cursorPointer.clientHeight / 2
        });
    }
    if (isMarking) {
        setPositioning(cursorTracker, {
            x: event.clientX - cursorTracker.clientWidth / 2,
            y: event.clientY - cursorTracker.clientHeight / 2
        });
    }
    if (isDragging) {
        isDragged = true;
        if (allowDrag) {
            let position = {
                x: numberOfPixel(baseMap.style.backgroundPositionX) - (oriPos.x - event.clientX),
                y: numberOfPixel(baseMap.style.backgroundPositionY) - (oriPos.y - event.clientY)
            };
            setBackgroundPosition(baseMap, position);
            setPositioning(markerBox, position);
            bgPos = position;
            oriPos = {x: event.clientX, y: event.clientY};
            syncTextForOffset();
            //syncSmallMap();
            //syncMarkerBox();
        }
    } else {
        if (isMarking) {
            setPositioning(markerPen, {
                x: event.clientX - markerImgIcon.mark.width / 2,
                y: event.clientY - markerImgIcon.mark.height
            });
        }
        let curPos = {
            x: event.clientX - bgPos.x,
            y: event.clientY - bgPos.y
        };
        let oldMarker = markerBox.querySelector('.marker.hover');
        if (oldMarker != undefined) {
            removeClass(oldMarker, 'hover');
            curMarker = undefined;
        }
        for (let i = 0; i < coordsList.length; i++) {
            let coords = coordsList[i];
            if (curPos.x >= coords.x && curPos.x <= coords.x + markerImgIcon.fixed.width && curPos.y >= coords.y && curPos.y <= coords.y + markerImgIcon.fixed.height) {
                curMarker = markerBox.querySelector('.marker[index="' + i + '"]');
                addClass(curMarker, 'hover');
                if (curMarker.centered == undefined) {
                    curMarker.centered = true;
                    centerInfo(curMarker);
                }
                break;
            }
        }
    }
}

function docMouseDown() {
    if (event.buttons == 1) {//左键
        if (event.target == maskLayer) {
            if (!isMarking) {
                isDragging = true;
                oriPos = {x: event.clientX, y: event.clientY};
                if (allowDrag) {
                    doc.style.cursor = 'grabbing';
                }
            }
        }
    }
}

function docMouseUp() {
    if (0 == event.button) {//左键
        qLatest.textContent = qPosition.textContent;
        if (isDragging) {
            isDragging = false;
            oriPos = undefined;
            if (allowDrag) {
                doc.style.cursor = '';
            }
            if (isDragged) {
                isDragged = false;//重置
            } else {
                //持久显示提示，并隐藏遮着层
                if (curMarker != undefined) {
                    isTipping = true;
                    addClass(markerBox, 'tipping');
                    doc.style.cursor = 'default';
                    addClass(curMarker, 'persisted');
                    whenClick(curMarker);
                    if (isCenterInfo) {
                        centerInfo(curMarker);
                    } else if (autoFocus) {
                        focusMarker(curMarker);
                    }
                    hide(maskLayer);
                }
            }
        } else if (isMarking) {
            let position = {
                x: event.clientX - markerImgIcon.fixed.width / 2 - numberOfPixel(baseMap.style.backgroundPositionX),
                y: event.clientY - markerImgIcon.fixed.height - numberOfPixel(baseMap.style.backgroundPositionY)
            };
            //此处 pickThreshold 亦或 -1 亦或 1+
            if (pickThreshold < 0 || coordsList.length < pickThreshold) {
                //不允许重复添加坐标
                if (!findMarker(position.x, position.y)) {
                    appendMarker(position);
                }
            } else {
                //更新最后一个坐标
                let marker = markerBox.querySelector('.marker:last-child');
                coordsList[getIndex(marker)] = position;
                setPositioning(marker, position);
            }
            newSimpleAnimation(cursorTracker, [
                {
                    transform: 'scale(0.125)'
                }, {
                    opacity: 0,
                    transform: 'scale(1)',
                    visibility: 'visible'
                }
            ], {
                duration: 256
            });
        } else if (isSetting) {
            let t = event.target;
            if (t == menuBox) {
                closeMenu();
                return;
            }
            if (t != bjwzSlot && t != xszbSlot && t != xsdhSlot && t != dtxsSlot && t != jzdqSlot && t != ksdwSlot && t != scwzSlot) {
                return;
            }
            let activeMarker = closeMenu();
            if (t == bjwzSlot) {
                toggleClass(t, 'checked');
                if (isMarking = hasClass(t, 'checked')) {
                    addClass(markerBox, 'marking');
                    //isDragging = false;
                    doc.style.cursor = 'default';
                    show(markerBox);
                    show(markerPen);
                    doc.onmousemove();//立即生效
                }
            } else if (t == xszbSlot) {
                toggleClass(t, 'checked');
                if (isCoefficientBox = hasClass(t, 'checked')) {
                    show(coefficientBox);
                    doc.onmousemove();//立即生效
                } else {
                    hide(coefficientBox);
                }
            } else if (t == xsdhSlot) {
                toggleClass(t, 'checked');
                isAnimation = hasClass(t, 'checked')
            } else if (t == dtxsSlot) {
                toggleClass(t, 'checked');
                if (isRippling = hasClass(t, 'checked')) {
                    addClass(markerBox, 'rippling');
                } else {
                    removeClass(markerBox, 'rippling');
                }
            } else if (t == jzdqSlot) {
                alignCenter();
            } else if (t == ksdwSlot) {
                //先决条件：仅有一个坐标
                gotoCoords(0);
            } else if (t == scwzSlot) {
                if (activeMarker != undefined) {
                    let persisted = hasClass(activeMarker, 'persisted');
                    removeCoords(getIndex(activeMarker));
                    if (persisted) {
                        switchToDragMode();
                    }
                }
            }
        }
    } else if (2 == event.button) {//右键
        if (event.buttons == 0) {
            if (isMarking) {
                isMarking = false;
                if (allowDrag) {
                    doc.style.cursor = '';
                }
                hide(markerPen);
                removeClass(bjwzSlot, 'checked');
                removeClass(markerBox, 'marking');
            } else if (allowMenu) {
                let marker = markerBox.querySelector('.marker.active');
                if (marker != undefined) {
                    removeClass(marker, 'active');
                }
                if (pickThreshold == 0) {
                    hide(bjwzSlot);
                    hide(dtxsSlot);
                } else {
                    show(dtxsSlot);
                    if (allowMark) {
                        show(bjwzSlot);
                    } else {
                        hide(bjwzSlot);
                    }
                }
                coordsList.length == 1 ? show(ksdwSlot) : hide(ksdwSlot);
                //注意：在不允许删除坐标的情况下，加 active 无意义。
                if (allowRemove && curMarker != undefined) {
                    removeClass(curMarker, 'hover');
                    addClass(curMarker, 'active');
                    show(scwzSlot);
                } else {
                    hide(scwzSlot);
                }
                //自动调整菜单栏宽度
                let funcList = menuPanel.querySelectorAll('.slot')[0].querySelectorAll('div');
                let validFunc = 0;
                for (let i = 0; i < funcList.length; i++) {
                    if (isVisible(funcList[i])) {
                        validFunc++;
                    }
                }
                menuPanel.style.width = 22 * validFunc + 38 + 'px';
                let curPos = {
                    x: event.clientX,
                    y: event.clientY - 20
                };
                setPositioning(menuPanel, curPos);
                addClass(menuPanel, 'o0');
                show(menuBox);
                isSetting = true;
                addClass(markerBox, 'setting');
                if (numberOfPixel(menuPanel.style.left) > doc.clientWidth - menuPanel.offsetWidth) {
                    curPos.x = event.clientX - menuPanel.offsetWidth;
                    setPositioning(menuPanel, curPos)
                }
                removeClass(menuPanel, 'o0');
            }
        }
    }
}

function docWheel() {
    let tmp = zoomLevel;
    if (event.deltaY < 0) {//放大
        tmp += zoomStep;
    } else if (event.deltaY > 0) {//缩小
        tmp -= zoomStep;
    }
    //检查缩放级别
    if (tmp > zoomInThreshold || tmp < zoomOutThreshold) {
        return;
    }
    zoomLevel = Number(tmp.toFixed(3));//更新缩放级别
    //log('Zoom', zoomLevel, 'x');
    setAttr(baseMap, 'zl', zoomLevel);
    //当前底图背景尺寸
    let baseMapBackgroundSize = baseMap.style.backgroundSize.split(' ');
    //计算光标相对于底图背景的距离
    let sideToCursor = {
        x: event.clientX - numberOfPixel(baseMap.style.backgroundPositionX),
        y: event.clientY - numberOfPixel(baseMap.style.backgroundPositionY)
    };
    //log('sideToCursor', sideToCursor);
    let cursorPosRate = {
        x: sideToCursor.x / numberOfPixel(baseMapBackgroundSize[0]),
        y: sideToCursor.y / numberOfPixel(baseMapBackgroundSize[1])
    };
    //log('cursorPosRate', cursorPosRate);
    //等比例缩放底图
    let width = baseImg.width * zoomLevel;
    let height = baseImg.height * zoomLevel;
    //缩放底图
    baseMap.style.backgroundSize = width + 'px ' + height + 'px';
    //计算光标相对于底图的偏移量百分比
    baseMap.style.backgroundPosition = (event.clientX - width * cursorPosRate.x) + 'px ' + (event.clientY - height * cursorPosRate.y) + 'px';
}

function docKeyUp() {
    if (event.keyCode == 112) {//F1
        showHelp();
    } else if (event.keyCode == 27) {//Escape
        switchToDragMode();
    } else if (event.keyCode == 13) {//Enter
        log(getCoords());
    }
}

function centerInfo(marker) {
    let info = marker.querySelector('.info-user');
    if (info == undefined) {
        return;
    }
    //计算 marker 相对于窗体的距离
    let markerToWin = {
        x: marker.offsetLeft + numberOfPixel(baseMap.style.backgroundPositionX),
        y: marker.offsetTop + numberOfPixel(baseMap.style.backgroundPositionY)
    };
    //计算 info 相对于窗体应偏移多少
    let infoToWin = {
        x: (doc.clientWidth - info.clientWidth) / 2,
        y: (doc.clientHeight - info.clientHeight) / 2
    };
    //计算 info 相对于 marker 应偏移多少
    let infoPos = {
        x: infoToWin.x - markerToWin.x,
        y: infoToWin.y - markerToWin.y
    };
    setPositioning(info, infoPos);
}

function focusMarker(marker) {
    //TODO: 待实现
    log('focusMarker', marker);
    log(marker.offsetLeft, marker.offsetTop);
}

function alignCenter() {
    let position = {
        x: (doc.clientWidth - imageSize.width) / 2,
        y: (doc.clientHeight - imageSize.height) / 2
    };
    bgPos = position;
    if (!isAnimation) {
        setBackgroundPosition(baseMap, position);
        syncTextForOffset();
        setPositioning(markerBox, position);
        return;
    }
    newSimpleAnimation(baseMap, [
        {
            backgroundPosition: position.x + 'px ' + position.y + 'px'
        }
    ], {
        duration: 1e3,
        easing: 'cubic-bezier(.68,-0.55,.27,1.55)'
    }, () => {
        setBackgroundPosition(baseMap, position);
        syncTextForOffset();
    });
    newSimpleAnimation(markerBox, [
        {
            left: position.x + 'px',
            top: position.y + 'px'
        }
    ], {
        duration: 1e3,
        easing: 'cubic-bezier(.68,-0.55,.27,1.55)'
    }, () => setPositioning(markerBox, position));
}

function setSubscript(index) {

}